/////////////////////////////////////////////////////////////////////////////
//
//  Fifteen Puzzle
//  Kolonitsky Developers Alliance (c) 2012
//  All rights received
//
/////////////////////////////////////////////////////////////////////////////

package model
{
    import flash.events.TimerEvent;
    import flash.utils.Timer;

    import model.types.DirectionName;

    import model.types.ApplicationStates;

    import model.vo.PuzzleVO;

    import org.puremvc.as3.patterns.proxy.Proxy;

    /**
     * Game object proxy.
     */
    public class GameProxy extends Proxy
    {
        public static const NAME:String = "GameProxy";

        public static const CHANGED:String = "gameChanged";

        //-----------------------------
        // game value object
        //-----------------------------

        public function get game():PuzzleVO
        {
            return data as PuzzleVO;
        }


        //-----------------------------
        // state property proxy
        //-----------------------------

        public function get state():uint
        {
            if (game)
                return game.state;
            return ApplicationStates.GAME_NONE;
        }

        public function set state(value:uint):void
        {
            if (game)
            {
                game.state = value;
                dirty = true;
            }
            else
            {
                throw new Error("Game object is not defined. Access to state field is failure.");
            }
        }


        //-----------------------------
        // game duration
        //-----------------------------

        public function get duration():Number
        {
            if (game)
                return game.duration;
            return 0;
        }


        //-------------------------------------------------------------------
        // Methods
        //-------------------------------------------------------------------

        public function GameProxy()
        {
            super(NAME, new PuzzleVO());
            updateTimer = new Timer(Config.UPDATE_DELAY);
            updateTimer.addEventListener(TimerEvent.TIMER, updateTimerHandler);
        }

        public function init():void
        {
            updateTimer.start();
        }

        public function createGame():void
        {
            state = ApplicationStates.GAME_INITIALIZE;
            game.duration = 0;
            game.stepCount = 0;
            game.fields = new Vector.<uint>(Config.BOARD_CELLS);
            for (var i:int = 0; i < Config.BOARD_CELLS; i++)
                game.fields[i] = i;
            emptyCellIndex = 15;
        }

        public function moveTo(direction:String):void
        {
            var resultIndex:int = emptyCellIndex;
            var side:int = Config.BOARD_WIDTH;
            var rowIndex:int = (emptyCellIndex - (emptyCellIndex % side)) / side;
            var newRow:int = 0;

            switch (direction)
            {
                case DirectionName.TOP:
                {
                    resultIndex = emptyCellIndex - side;
                    resultIndex = resultIndex < 0 ? emptyCellIndex : resultIndex;
                    break;
                }

                case DirectionName.RIGHT:
                {
                    resultIndex = emptyCellIndex + 1;
                    newRow = (resultIndex - (resultIndex % side)) / side;
                    resultIndex = newRow == rowIndex ? resultIndex : emptyCellIndex;
                    break;
                }

                case DirectionName.BOTTOM:
                {
                    resultIndex = emptyCellIndex + side;
                    resultIndex = resultIndex > Config.BOARD_CELLS ? emptyCellIndex : resultIndex;
                    break;
                }

                case DirectionName.LEFT:
                {
                    resultIndex = emptyCellIndex - 1;
                    newRow = (resultIndex - (resultIndex % side)) / side;
                    resultIndex = newRow == rowIndex ? resultIndex : emptyCellIndex;
                    break;
                }
            }

            if (resultIndex != emptyCellIndex)
            {
                game.fields[emptyCellIndex] = game.fields[resultIndex];
                game.fields[resultIndex] = Config.EMPTY_CELL_CODE;
                if (game.state == ApplicationStates.GAME_GAME)
                    game.stepCount++;
                emptyCellIndex = resultIndex;
                dirty = true;
            }
        }

        //-------------------------------------------------------------------
        //
        //  Private
        //
        //-------------------------------------------------------------------

        private var updateTimer:Timer = null;
        private var emptyCellIndex:int = -1;
        private var dirty:Boolean = true;

        private function updateTimerHandler(event:TimerEvent):void
        {
            if (game.state == ApplicationStates.GAME_GAME)
            {
                game.duration += Config.UPDATE_DELAY;
                dirty = true;
            }

            if (dirty)
                sendNotification(CHANGED, game);

            if (game.state != ApplicationStates.GAME_GAME)
                return;

            // Check puzzle completeness
            for (var i:int = 0; i < Config.BOARD_CELLS; i++)
                if (game.fields[i] != i)
                    return;

            // Change state on game complete
            game.isComplete = true;
            state = ApplicationStates.GAME_RESULT;
        }

    }
}
